#ifndef SECTIONOFPIPE_H
#define SECTIONOFPIPE_H
#define _USE_MATH_DEFINES

#include "geometry/wsyline.h"
#include "geometry/wsyplane.h"
#include "geometry/wsyring.h"
#include "geometry/geometry.h"
#include <math.h>

/** 王诗阳的管道状路径几何体 */
class WSYSectionOfPipe: public Geometry {
public:
    /**
     * 实例化一个管道状路径
     * @param path 路径
     * @param pipeRadius 管道半径
     * @param segment 分段
     */
    WSYSectionOfPipe(QList<Vertex3D *> *path, double pipeRadius, int segment) {
        localPath = path;
        localPipeRadius = pipeRadius;
        localSegment = segment;

        ring = new WSYRing(1, pipeRadius, segment);
        ring->generate();

        allReferenceVertices = new QList <QList<GLfloat *> *>();
        allReferenceLines = new QList <QList<WSYLine *> *>();
        referencePlanes = new QList <WSYPlane *>();
    }
    ~WSYSectionOfPipe(){

    }
    /** 生成管状几何体 */
    virtual void generate() {
        //圆环的节点
        QList<GLfloat *> *verticesOfRing = ring->getVertices();
        //圆环的边缘和数量
        QList<GLint *> *edges = ring->getEdges();
        QList<int> *edgeCounts = ring->getEdgeCounts();

        GLfloat *cXDirection = Math3D::newInstance(); //current X direction
        GLfloat *cYDirection = Math3D::newInstance(); //current Y direction

        GLfloat *rCXDirection = Math3D::newInstance(); //referenced current X direction
        GLfloat *rCYDirection = Math3D::newInstance();

        GLfloat *addedVector = Math3D::newInstance();

        GLfloat *upright = Math3D::newInstanceWithValue(0, 0, 1);

        GLfloat *leftDirection = Math3D::newInstance();
        GLfloat *rightDirection = Math3D::newInstance();


        int pathCount = localPath->count();
        for (int p = 0; p < pathCount; p++) { //p: path
            //获取当前节点中所对应的参考节点，如果没有，则另建一个
            QList <GLfloat *> *referenceVertices = allReferenceVertices->at(p);
            if (referenceVertices == nullptr) { //如果没有，则新建一个并记录在原数组中
                referenceVertices = new QList <GLfloat *>();
                allReferenceVertices->append(referenceVertices);
            }

            //获取当前节点中所对应的参考射线（经过当前节点，与当前的管道方向一致）
            QList <WSYLine *> *referenceLines = allReferenceLines->at(p);
            if (referenceLines == nullptr) {
                referenceLines = new QList <WSYLine *>();
                allReferenceLines->append(referenceLines);
            }

            //获取当前节点中所对应的参考平面（如果没有，则另建一个）
            WSYPlane *referencePlane = referencePlanes->at(p);
            if (referencePlane == nullptr) {
                GLfloat *vertex = Math3D::newInstanceWithValue(0, 0, 0);
                GLfloat *normal = Math3D::newInstanceWithValue(0, 0, 1);
                referencePlane = new WSYPlane(vertex, normal);
                referencePlanes->append(referencePlane);
            }

            Vertex3D *thisVertex = localPath->at(p);
            GLfloat *thisVertex2 = Math3D::newInstanceWithVertex3D(thisVertex);
            Vertex3D *nextVertex = localPath->at(p + 1);

            if (p == 0) {//左边没有方向的时候
                Math3D::directionNew(thisVertex, nextVertex, rightDirection);

                referencePlane->setVertex(thisVertex->getX(), thisVertex->getY(), thisVertex->getZ());
                referencePlane->setNormal(rightDirection[0], rightDirection[1], rightDirection[2]);

                //当前方向垂直
                if (rightDirection[0] == 0 && rightDirection[1] == 0) {
                    Math3D::setNewValue(1, 0, 0, cXDirection);
                } else {
                    Math3D::crossVectorsNew(rightDirection, upright, cXDirection);
                    //将X方向标准化
                    Math3D::normalizeWithChanging(cXDirection);
                }

                Math3D::crossVectorsNew(cXDirection, rightDirection, cYDirection);
                //将Y方向标准化
                Math3D::normalizeWithChanging(cYDirection);

                //遍历圆环的所有节点
                for (int v = 0; v < verticesOfRing->count(); v++) {//v: vertex (of ring)
                    GLfloat *singleVertex = verticesOfRing->at(v);
                    Math3D::multiplyVectorNew(singleVertex[0], cXDirection, rCXDirection);
                    Math3D::multiplyVectorNew(singleVertex[1], cYDirection, rCYDirection);
                    Math3D::addVectorsNew(rCXDirection, rCYDirection, addedVector);

                    GLfloat *singleReferenceVertex = referenceVertices->at(v);
                    if (singleReferenceVertex == nullptr) {
                        singleReferenceVertex = Math3D::newInstance();
                        referenceVertices->append(singleReferenceVertex);
                    }

                    Math3D::setNewValue(
                            thisVertex->getX() + addedVector[0],
                            thisVertex->getY() + addedVector[1],
                            thisVertex->getZ() + addedVector[2], singleReferenceVertex);

                    WSYLine *line = referenceLines->at(v);
                    if (line == nullptr) {
                        line = new WSYLine(thisVertex2, rightDirection);
                        referenceLines->append(line);
                    }
                }
            } else { //p的值不为0
                Vertex3D *prevVertex = localPath->at(p - 1); //prev: previous

                QList<GLfloat *> *prevReferenceVertices = allReferenceVertices->at(p - 1);
                QList<WSYLine *> *prevReferenceLines = allReferenceLines->at(p - 1);

                Math3D::directionNew(prevVertex, thisVertex, leftDirection);
                Math3D::directionNew(thisVertex, nextVertex, rightDirection);
                Math3D::normalizeWithChanging(leftDirection);
                Math3D::normalizeWithChanging(rightDirection);

                GLfloat *planeNormal = Math3D::newInstance();
                if (pathCount != pathCount - 2)
                    Math3D::addVectorsNew(leftDirection, rightDirection, planeNormal);
                else
                    Math3D::setNewValueWithVector(rightDirection, planeNormal);

                referencePlane->setVertex(thisVertex->getX(), thisVertex->getY(), thisVertex->getZ());
                referencePlane->setNormal(planeNormal[0], planeNormal[1], planeNormal[2]);

                for (int v = 0; v < prevReferenceLines->count(); v++) {
                    WSYLine *line = prevReferenceLines->at(v);

                    GLfloat *referenceVertex = referenceVertices->at(v);
                    if (referenceVertex == nullptr) {
                        referenceVertex = Math3D::newInstance();
                        referenceVertices->append(referenceVertex);
                    }

                    GLfloat *r = Math3D::ComputeJoinedVertexOfLineAndPlane(line, referencePlane);
                    Math3D::setNewValueWithVector(r, referenceVertex);
                }
            }
        }
    }
private:
    QList<Vertex3D *> *localPath;
    QList <QList<GLfloat *> *> *allReferenceVertices;
    QList <QList<WSYLine *> *> *allReferenceLines;
    QList <WSYPlane *> *referencePlanes;

    double localPipeRadius;
    int localSegment;

    WSYRing *ring;
};

#endif // SECTIONOFPIPE_H
